﻿using System;
using System.Collections;
using System.Text;
using System.IO;

class ToolKit
{
    public static string _AppLog = string.Empty;
    public static string __message;

    /// <summary>
    /// 
    /// Override for LogIt.  Call the function with no parameters to get a blank line in 
    /// the log
    /// 
    /// </summary>
    public static void LogIt()
    {
        using (StreamWriter sw = new StreamWriter(_AppLog, true))
        {
           //sw.Write("\n");
           sw.WriteLine();
           sw.Flush();
        }
    }

    /// <summary>
    /// 
    /// "Meat" of log it.  Takes any number of paramaters of any type and prints them
    /// all out on one line preceded by a timestamp.
    /// 
    /// TODO:  I'm sure there are other data types that can be added but these are the
    ///        ones I needed right now. I'll add the others when I find a need.
    /// 
    /// </summary>
    /// <param name="vals"></param>
    public static void LogIt(params object[] vals)
    {
        using (StreamWriter sw = new StreamWriter(_AppLog, true))
        {
            DateTime dt = DateTime.Now;
            sw.Write(dt.ToString("MM-dd-yyyy HH:mm:ss "));

            foreach (object o in vals)
            {
                if (o.GetType() == typeof(string)) sw.Write("{0} ", (string)o);
                else if (o.GetType() == typeof(int)) sw.Write("{0} ", (int)o);
                else if (o.GetType() == typeof(Int16)) sw.Write("{0} ", (Int16)o);
                else if (o.GetType() == typeof(UInt16)) sw.Write("{0} ", (UInt16)o);
                else if (o.GetType() == typeof(Int32)) sw.Write("{0} ", (Int32)o);
                else if (o.GetType() == typeof(UInt32)) sw.Write("{0} ", (UInt32)o);
                else if (o.GetType() == typeof(Int64)) sw.Write("{0} ", (Int64)o);
                else if (o.GetType() == typeof(UInt64)) sw.Write("{0} ", (UInt64)o);
                else if (o.GetType() == typeof(double)) sw.Write("{0,5:N} ", (double)o);
                else if (o.GetType() == typeof(float)) sw.Write("{0,5:N} ", (float)o);
                else if (o.GetType() == typeof(bool)) sw.Write("{0} ", (bool)o);
                else if (o.GetType() == typeof(long)) sw.Write("{0} ", (long)o);
                else sw.Write("Data type not handled by Toolkit.LogIt(): ", o.GetType());
            }
            //sw.Write("\n");
            sw.WriteLine();
            sw.Flush();
        }
    }

    /// <summary>
    ///  
    /// FileInUse() checks to see if the file found at "path" is in use by another process.
    /// 
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static bool FileInUse(string path)
    {
        try
        {
            //Just opening the file as open/create
            using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
            {
                //If required we can check for read/write by using fs.CanRead or fs.CanWrite
            }
            return false;
        }
        catch (IOException ex)
        {
            //check if message is for a File IO
            __message = ex.Message.ToString();
            if (__message.Contains("The process cannot access the file"))
            {
                LogIt("File is in use by another process.", path);
                return true;
            }
            else
                throw;
        }
    }

    /// <summary>
    /// 
    /// CalCheckSum() calculates the checksum(s) for the file "f" and populates PrimaryCheckSum 
    /// and SecondaryChecksum passed it by ref.
    /// 
    /// </summary>
    /// <param name="f"></param>
    /// <param name="PrimaryCheckSum"></param>
    /// <param name="SecondaryCheckSum"></param>
    public static void depCalCheckSum(string f, ref int PrimaryCheckSum, ref int SecondaryCheckSum )
    {
        int NextByte = 0;
        long checksum = (long)Math.Pow(2, 16)-1;
        long secondary = (long)Math.Pow(2, 16)-1;

        FileStream file = new FileStream(f, FileMode.Open);
        while (NextByte != -1)
        {
            NextByte = file.ReadByte();
            if (NextByte != -1)
            {
                checksum += NextByte;
                secondary += checksum;
            }
            PrimaryCheckSum = (int)(checksum % ((long)Math.Pow(2, 16) - 1));
            SecondaryCheckSum = (int)(secondary % ((long)Math.Pow(2, 16) - 1));

        }
        file.Close();
    }

    /// <summary>
    /// 
    /// CalCheckSum calculates checksum for file passed it (f) and returns UInt64 checksum. 
    /// 
    /// </summary>
    /// <param name="f"></param>
    /// <returns></returns>
    public static UInt64 CalCheckSum(string f)
    {
        UInt32 primary = 0;
        UInt32 secondary = 0;

        UInt32 ByteInteger = 0;
        
        // Use BaseStream.
        using (BinaryReader b = new BinaryReader(File.Open(f, FileMode.Open)))
        {
            // Position and length variables.
            int pos = 0;
            int length = (int)b.BaseStream.Length;

            while (pos < length)
            {
                try
                {
                    // Read integer.
                    ByteInteger = BitConverter.ToUInt32(b.ReadBytes(4), 0);
                    primary   = (UInt32) ((primary   + ByteInteger ) % ( (int) (Math.Pow(2, 32) - 1)));
                    secondary = (UInt32) ((secondary + primary)   % ( (int) (Math.Pow(2, 32) - 1)));
                    //LogIt(ByteInteger, primary, secondary);
                }
                catch
                {
                    //End of File
                }

                // Advance position variable.
                pos += sizeof(int);
            }
        }
        return (UInt64) (primary | secondary * ((int) ((Math.Pow(2,32))-1)));
    }

    /// <summary>
    /// 
    /// DirSearch() executes a recursive directory search for all files in a given
    /// directory structure.  It calls UserClass.AddFile for each file found.  
    /// 
    /// UserClass.AddFile must exist in program in order for DirSearch() to function properly without 
    /// modification.  UserClass.AddFile can be written any way the user sees fit.
    /// 
    /// </summary>
    /// <param name="sDir"></param>
    /// <param name="ListOfObjects"></param>
    public static void DirSearch(string sDir, ref ArrayList ListOfObjects)
    {
        try
        {
            // Loop through each directory
            foreach (string dir in Directory.GetDirectories(sDir))
            {
                try
                {

                    // Get all files in current directory
                    foreach (string file in Directory.GetFiles(dir))
                    {
                        // AddFile to list of CheckSum objects
                        if (!ToolKit.FileInUse(file))
                        {
                            UserClass.AddFile(file, ref ListOfObjects);
                        }
                    }
                    // Recursive call to DirSearch()
                    DirSearch(dir, ref ListOfObjects);
                }
                catch (System.Exception excpt)
                {
                    Console.WriteLine(excpt.Message);
                }
            }
        }
        catch (System.Exception excpt)
        {
            Console.WriteLine(excpt.Message);
        }
    }

    public static int CalcDivisor(long FileSize)
    {
        int i = 0;
        while ((FileSize >= 1024) && (i < 3)) 
        {
            FileSize = FileSize / 1024;
            i++;
        }

        return i;
    }
}